package org.fujene.term.tool;

/**
 * 文字列の変換やエスケープ処理を行います。
 * 
 * @author yasuda
 */
public class Escaper{

	private static char[] GAP_OF_MAPPING_SJIS = { '\u301c', // ～ (1-33, WAVE
															// DASH)
			'\u2016', // ∥ (1-34, DOUBLE VERTICAL LINE)
			'\u2212', // － (1-61, MINUS SIGN)
			'\u00a2', // ￠ (1-81, CENT SIGN)
			'\u00a3', // ￡ (1-82, POUND SIGN)
			'\u00ac', // ￢ (2-44, NOT SIGN)
			'\u2014' // ― (1-29, EM DASH)
	};

	private static char[] GAP_OF_MAPPING_MS932 = { '\uff5e', // ～ (1-33, WAVE
																// DASH)
			'\u2225', // ∥ (1-34, DOUBLE VERTICAL LINE)
			'\uff0d', // － (1-61, MINUS SIGN)
			'\uffe0', // ￠ (1-81, CENT SIGN)
			'\uffe1', // ￡ (1-82, POUND SIGN)
			'\uffe2', // ￢ (2-44, NOT SIGN)
			'\u2015' // ― (1-29, EM DASH)
	};

	private static char[] ENTITY_REF_ORIGIN = { '&', '\"', '<', '>' };

	private static String[] ENTITY_REF = { "&amp;", "&quot;", "&lt;", "&gt;" };

	private static char[] JSON_ESCAPE_MAP_ORIGIN = { '\"', '\'', '\r', '\n' };

	private static String[] JSON_ESCAPE_MAP = { "\\\"", "\\\'", "", "" };

	/**
	 * Javaのマッピングテーブルがズレている文字のマッピングを強制的に変換
	 * （SJISからMS932へ）
	 * 
	 * @param str
	 * @return
	 */
	public static String convertSJISToMS932(String str){
		if (str == null) return str;

		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			boolean isMapped = false;
			for(int i = 0; i < GAP_OF_MAPPING_SJIS.length; i++){
				if (c == GAP_OF_MAPPING_SJIS[i]){
					builder.append(GAP_OF_MAPPING_MS932[i]);
					isMapped = true;
					break;
				}
			}
			if (!isMapped) builder.append(c);
		}
		return builder.toString();
		/*//dirty code
		 * return str.replaceAll("\u301c", "\uff5e") //～ (1-33, WAVE DASH)
		 * .replaceAll("\u2016", "\u2225") //∥ (1-34, DOUBLE VERTICAL LINE)
		 * .replaceAll("\u2212", "\uff0d") //－ (1-61, MINUS SIGN)
		 * .replaceAll("\u00a2", "\uffe0") //￠ (1-81, CENT SIGN)
		 * .replaceAll("\u00a3", "\uffe1") //￡ (1-82, POUND SIGN)
		 * .replaceAll("\u00ac", "\uffe2") //￢ (2-44, NOT SIGN)
		 * .replaceAll("\u2014", "\u2015"); //― (1-29, EM DASH) */
	}

	/**
	 * Javaのマッピングテーブルがズレている文字のマッピングを強制的に変換
	 * （MS932からSJISへ）
	 * 
	 * @param str
	 * @return
	 */
	public static String convertMS932ToSJIS(String str){
		if (str == null) return str;

		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			boolean isMapped = false;
			for(int i = 0; i < GAP_OF_MAPPING_MS932.length; i++){
				if (c == GAP_OF_MAPPING_MS932[i]){
					builder.append(GAP_OF_MAPPING_SJIS[i]);
					isMapped = true;
					break;
				}
			}
			if (!isMapped) builder.append(c);
		}
		return builder.toString();
		/*//dirty code
		 * return str.replaceAll("\uff5e", "\u301c") //～ (1-33, WAVE DASH)
		 * .replaceAll("\u2225", "\u2016") //∥ (1-34, DOUBLE VERTICAL LINE)
		 * .replaceAll("\uff0d", "\u2212") //－ (1-61, MINUS SIGN)
		 * .replaceAll("\uffe0", "\u00a2") //￠ (1-81, CENT SIGN)
		 * .replaceAll("\uffe1", "\u00a3") //￡ (1-82, POUND SIGN)
		 * .replaceAll("\uffe2", "\u00ac") //￢ (2-44, NOT SIGN)
		 * .replaceAll("\u2015", "\u2014"); //― (1-29, EM DASH) */
	}

	/**
	 * 文字列中の実体参照文字をエスケープ処理します。
	 * 
	 * @param str
	 * @return 実体参照文字をエスケープした後の文字列
	 */
	public static String escapeEntityRef(String str){
		if (str == null) return str;

		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			boolean isMapped = false;
			for(int i = 0; i < ENTITY_REF_ORIGIN.length; i++){
				if (c == ENTITY_REF_ORIGIN[i]){
					builder.append(ENTITY_REF[i]);
					isMapped = true;
					break;
				}
			}
			if (!isMapped) builder.append(c);
		}
		return builder.toString();
	}

	/**
	 * 文字列中の実体参照文字をアンエスケープ処理します。
	 * 
	 * @param str
	 * @return 実体参照文字をエスケープした後の文字列
	 */
	public static String unescapeEntityRef(String str){
		if (str == null){
			return str;
		}

		str = str.replaceAll("&nbsp;", " ");
		str = str.replaceAll("&amp;", "&");
		str = str.replaceAll("&#38;", "&");
		str = str.replaceAll("&rsquo;", "'");
		str = str.replaceAll("&#39;", "'");
		str = str.replaceAll("&quot;", "\"");
		str = str.replaceAll("&#34;", "\"");
		str = str.replaceAll("&lt;", "<");
		str = str.replaceAll("&#60;", "<");
		str = str.replaceAll("&gt;", ">");
		str = str.replaceAll("&#62;", ">");
		str = str.replaceAll("&yen;", "\\\\");
		str = str.replaceAll("&#59;", ";");
		str = str.replaceAll("&#92;", "\\\\");
		str = str.replaceAll("&hellip;", "…");
		str = str.replaceAll("&rarr;", "→");
		str = str.replaceAll("&larr;", "←");
		str = str.replaceAll("&acute;", "´");
		str = str.replaceAll("&rdquo;", "“");
		str = str.replaceAll("&ldquo;", "”");
		// TODO 本当はもっと多い。があまりにも多すぎるので、表示上問題がおきないレベルで対応

		return str;
	}

	/**
	 * 文字列中の実体参照文字のうち、空白文字のみをエスケープ処理します。
	 * 
	 * @param str
	 * @return 実体参照文字をエスケープした後の文字列
	 */
	public static String escapeEntityRefOnlySpace(String str){
		if (str == null) return null;

		StringBuilder result = new StringBuilder();
		char[] chars = str.toCharArray();
		boolean isTag = false;
		for(int i = 0; i < chars.length; i++){
			if (chars[i] == '<'){
				isTag = true;
			}else if (chars[i] == '>'){
				isTag = false;
			}

			if (isTag){
				result.append(chars[i]);
			}else{
				result.append(chars[i] == '\u0020' ? "&nbsp;" : chars[i]);
			}
		}
		return result.toString();

	}

	/**
	 * JSONデータ形式の値として使用ができない文字列をエスケープ処理、もしくは除去するメソッドです。
	 * エスケープ：ダブルコーテーション、シングルコーテーション
	 * 除去：改行文字、復帰文字
	 * 
	 * @param str
	 * @return
	 */
	public static String escapeForJson(String str){
		if (str == null) return str;

		StringBuilder builder = new StringBuilder();
		for(char c: str.toCharArray()){
			boolean isMapped = false;
			for(int i = 0; i < JSON_ESCAPE_MAP_ORIGIN.length; i++){
				if (c == JSON_ESCAPE_MAP_ORIGIN[i]){
					builder.append(JSON_ESCAPE_MAP[i]);
					isMapped = true;
					break;
				}
			}
			if (!isMapped) builder.append(c);
		}
		return builder.toString();

		/*//dirty code
		 * str = str.replace("\"", "\\\"");
		 * str = str.replace("\'", "\\\'");
		 * str = str.replace("\r", "");
		 * str = str.replace("\n", "");
		 * return str; */
	}

	/**
	 * brタグを改行文字に置換します。
	 */
	public static String convertBrToNewLine(String str){
		if (str == null){
			return str;
		}

		return str.replaceAll("<[b|B][r|R][\\s]*/?>", System.getProperty("line.separator"));
	}
}
